﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace eProject.Model
{
    public class Schedule
    {
        private readonly Storage _storage;

        private readonly ScheduleSettings _settings;        
        
        public Schedule(Storage storage)
        {
            _storage = storage;
            _settings = storage.ScheduleSettings;
            _storage.Employees.CollectionChanged += EmployeesCollectionChanged;
            _storage.ScheduleItems.CollectionChanged += ScheduleItemsCollectionChanged;
            _storage.ScheduleSettings.SettingsChanged += delegate { InvokeScheduleChanged(); };
        }

        public double GetManHours(Employee employee, Period period)
        {
            if (employee == null) throw new ArgumentNullException("employee");
            if (period == null) throw new ArgumentNullException("period");

            var begin = period.Begin.Date;

            var end = period.End.Date;

            var dt = begin;
            double manHours = 0;

            while (dt < end)
            {
                manHours += GetManHours(employee, dt);
                dt = dt.AddDays(1);

            }
            return manHours;
        }

        public double GetManHours(Employee employee, DateTime date)
        {
            var emplemail = employee != null ? employee.Email : "";
            var exceptions = _storage.ScheduleItems.Where(i => i.IsApplicable(date, emplemail)).ToList();
            if (exceptions.Count > 0)
            {
                var lastException = exceptions.Last();
                return lastException.ManHours;
            }
            return _settings.GetManHours(date);
        }

        public double GetManHours(DateTime date)
        {
            return GetManHours(null, date);
        }

        public bool IsDayOff(DateTime date)
        {
            return GetManHours(date) == 0;
        }

        private void EmployeesCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            
            if (args.NewItems != null)
            {
                var newItems = args.NewItems.Cast<Employee>();
                foreach (var item in newItems)
                {
                    item.EmailChanged += EmployeeEmailChanged;
                }
            }
            if (args.OldItems != null)
            {
                var oldItems = args.OldItems.Cast<Employee>();
                foreach (var item in oldItems)
                {
                    item.EmailChanged -= EmployeeEmailChanged;
                }
            }
        }

        private void EmployeeEmailChanged(Employee employee, string oldEmail)
        {
            var items = _storage.ScheduleItems.Where(i => i.IsApplicable(employee.Email)).ToList();
            foreach (var item in items)
            {
                item.ChangeEmail(oldEmail, employee.Email);
            }
        }

        private void ScheduleItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            var newEmployees = new List<Employee>(0);
            var oldEmployees = new List<Employee>(0);
            if (args.NewItems != null)
            {
                var newItems = args.NewItems.Cast<ScheduleItem>();
                newEmployees = newItems.SelectMany(
                                   i => i.EmployeesEmails.Count() == 0 ? _storage.Employees :
                                         i.EmployeesEmails.SelectMany(e => _storage.Employees.Where(em => em.Email == e)))
                                         .Distinct().ToList();

                
            }
            if (args.OldItems != null)
            {
                var oldItems = args.OldItems.Cast<ScheduleItem>();
                oldEmployees = oldItems.SelectMany(
                                   i => i.EmployeesEmails.Count() ==0?_storage.Employees:
                                         i.EmployeesEmails.SelectMany(e => _storage.Employees.Where(em => em.Email == e)))
                                         .Distinct().ToList();
            }

            var employeesChanded = newEmployees.Union(oldEmployees).ToList();
            InvokeIndividualRulesChanged(employeesChanded);
            InvokeScheduleChanged();
        }

        internal event EventHandler<IEnumerable<Employee>> IndividualRulesChanged;

        public event EventHandler ScheduleChanged;

        private void InvokeScheduleChanged()
        {
            var handler = ScheduleChanged;
            if (handler != null) handler();
        }

        private void InvokeIndividualRulesChanged(IEnumerable<Employee> item)
        {
            var handler = IndividualRulesChanged;
            if (handler != null) handler(item);
        }
    }
}
